# api_bridge/js_api.py
# Defines all Python-side functions that will be exposed to the JavaScript frontend via pywebview.api.
# Detailed implementation will follow based on design documents.

from core.data_models import ChatMessage, GlobalTag, LLMLogEntry, WhiteboardItem # Added WhiteboardItem
from services.db_service import DBService, DB_NAME # Added DBService and DB_NAME for new connection
import uuid
import os # For path manipulation
from datetime import datetime, timezone, timedelta # Added timedelta for mock
from typing import List, Dict # Added List and Dict
import threading # For checking thread ID

class PywebviewApi:
    def __init__(self, db_service, agent_manager, chatroom_state):
        self.db_service = db_service
        self.agent_manager = agent_manager
        self.chatroom_state = chatroom_state
        print("PywebviewApi initialized with core services.")

    def handle_human_message_submission(self, message_text: str, tags: list[str]) -> dict:
        """
        Handles message submission from the human user in the UI.
        Processes the message, stores it, and triggers agent listening.
        Returns a status or the new message object.
        """
        print(f"Executing handle_human_message_submission in thread: {threading.get_ident()}")
        print(f"JS API: Received human message: '{message_text}' with tags: {tags}.")

        sender_id = "human_user_main" # MVP assumption
        sender_participant = self.chatroom_state.get_participant_by_id(sender_id)
        
        if not sender_participant:
            error_msg = f"Sender participant '{sender_id}' not found."
            print(f"Error: {error_msg}")
            return {"success": False, "error": error_msg}

        sender_nickname = sender_participant.nickname

        new_message = ChatMessage(
            message_id=str(uuid.uuid4()),
            participant_id=sender_id,
            sender_nickname=sender_nickname,
            content=message_text,
            timestamp=datetime.now(timezone.utc),
            tags=tags if tags else []
        )

        # Use a new DB connection for this thread
        db_for_thread = None
        try:
            db_for_thread = DBService(db_name=DB_NAME)
            db_for_thread.connect()

            # 1. Add message to DB using the thread-specific connection
            db_for_thread.add_chat_message(new_message)
            
            # 2. Update in-memory chat history in ChatroomState (this is thread-safe for list append)
            self.chatroom_state.public_chat_history.append(new_message)
            
            # 3. Update global tags using the thread-specific connection and update in-memory ChatroomState
            if new_message.tags:
                for raw_tag in new_message.tags:
                    normalized_tag = raw_tag.lower().strip() # Consistent normalization
                    if not normalized_tag:
                        continue
                    
                    db_for_thread.add_or_update_global_tag(normalized_tag, new_message.timestamp)
                    updated_tag_obj_from_db = db_for_thread.get_global_tag(normalized_tag)
                    if updated_tag_obj_from_db:
                        self.chatroom_state.global_tags[normalized_tag] = updated_tag_obj_from_db
                    else: # Should not happen if add_or_update worked
                         # Create a new GlobalTag object for in-memory state if somehow not in DB
                         self.chatroom_state.global_tags[normalized_tag] = GlobalTag(
                             tag_name=normalized_tag,
                             occurrence_count=1, # Assuming first occurrence if not found
                             first_seen_timestamp=new_message.timestamp,
                             last_seen_timestamp=new_message.timestamp
                         )
                         print(f"Warning: Tag '{normalized_tag}' was not found in DB after update attempt by API, created in-memory.")


            print(f"Message '{new_message.message_id}' by '{sender_nickname}' processed in API thread. DB & memory updated.")
            
            return {
                "success": True,
                "message": {
                    "message_id": new_message.message_id,
                    "sender_nickname": new_message.sender_nickname,
                    "content": new_message.content,
                    "timestamp": new_message.timestamp.isoformat(),
                    "tags": new_message.tags
                }
            }
        except Exception as e:
            error_msg = f"Error processing message in API thread: {e}"
            print(error_msg)
            import traceback
            traceback.print_exc()
            return {"success": False, "error": error_msg}
        finally:
            if db_for_thread:
                db_for_thread.close()
                print(f"Closed DB connection for thread {threading.get_ident()}")

    def add_whiteboard_item(self, file_path: str) -> dict:
        """
        Handles request from UI to add a new file reference to the whiteboard.
        Creates a WhiteboardItem, adds it via ChatroomState, and returns the updated list.
        """
        print(f"Executing add_whiteboard_item in thread: {threading.get_ident()}")
        print(f"JS API: Received request to add whiteboard item: {file_path}")
        try:
            if not file_path or not isinstance(file_path, str):
                return {"success": False, "error": "Invalid file path provided.", "whiteboard_items": self._get_whiteboard_items_for_ui()}

            display_name = os.path.basename(file_path)
            uploader_participant_id = "human_user_main" # MVP: Hardcoded

            new_item = WhiteboardItem(
                display_name=display_name,
                full_local_path=file_path,
                uploader_participant_id=uploader_participant_id
            )
            
            self.chatroom_state.add_whiteboard_item(new_item)
            
            print(f"JS API: Whiteboard item '{new_item.reference_id}' for '{file_path}' added.")
            return {"success": True, "whiteboard_items": self._get_whiteboard_items_for_ui()}
        except Exception as e:
            error_msg = f"Error adding whiteboard item in API: {e}"
            print(error_msg)
            import traceback
            traceback.print_exc()
            return {"success": False, "error": error_msg, "whiteboard_items": self._get_whiteboard_items_for_ui()}

    def remove_whiteboard_item(self, reference_id: str) -> dict:
        """
        Handles request from UI to remove a file reference from the whiteboard.
        Removes the item via ChatroomState and returns the updated list.
        """
        print(f"Executing remove_whiteboard_item in thread: {threading.get_ident()}")
        print(f"JS API: Received request to remove whiteboard item: {reference_id}")
        try:
            if not reference_id or not isinstance(reference_id, str):
                return {"success": False, "error": "Invalid reference_id provided.", "whiteboard_items": self._get_whiteboard_items_for_ui()}

            success = self.chatroom_state.remove_whiteboard_item(reference_id)
            if success:
                print(f"JS API: Whiteboard item '{reference_id}' removed.")
                return {"success": True, "whiteboard_items": self._get_whiteboard_items_for_ui()}
            else:
                print(f"JS API: Failed to remove whiteboard item '{reference_id}'. It might not exist.")
                return {"success": False, "error": "Item not found or failed to remove.", "whiteboard_items": self._get_whiteboard_items_for_ui()}
        except Exception as e:
            error_msg = f"Error removing whiteboard item in API: {e}"
            print(error_msg)
            import traceback
            traceback.print_exc()
            return {"success": False, "error": error_msg, "whiteboard_items": self._get_whiteboard_items_for_ui()}

    def _get_whiteboard_items_for_ui(self) -> List[Dict]:
        """Helper to format whiteboard items for the UI."""
        items_for_ui = []
        for item in self.chatroom_state.whiteboard_items:
            items_for_ui.append({
                "reference_id": item.reference_id,
                "display_name": item.display_name,
                "full_local_path": item.full_local_path,
                "uploader_participant_id": item.uploader_participant_id,
                "timestamp_added": item.timestamp_added.isoformat()
            })
        return items_for_ui

    def get_initial_chatroom_data(self) -> dict:
        """
        Called by the UI on startup to fetch initial data like chat history, participants, whiteboard items, etc.
        """
        print(f"Executing get_initial_chatroom_data in thread: {threading.get_ident()}")
        print("JS API: Received request for initial chatroom data.")
        
        # 获取实际的参与者数据
        participants = []
        for participant_id, participant in self.chatroom_state.participants.items():
            participants.append({
                "participant_id": participant_id,
                "nickname": participant.nickname,
                "role": participant.role
            })
        
        # 获取聊天历史记录
        chat_history = []
        for msg in self.chatroom_state.public_chat_history:
            chat_history.append({
                "message_id": msg.message_id,
                "sender_nickname": msg.sender_nickname,
                "content": msg.content,
                "timestamp": msg.timestamp.isoformat(),
                "tags": msg.tags
            })
        
        # 获取全局标签
        global_tags = []
        for tag_name, tag_obj in self.chatroom_state.global_tags.items():
            global_tags.append({
                "tag": tag_name,
                "count": tag_obj.occurrence_count
            })
        
        # 返回完整的初始数据
        return {
            "chat_history": chat_history,
            "participants": participants,
            "whiteboard_items": self._get_whiteboard_items_for_ui(),
            "global_tags": global_tags
        }

    def get_agent_logs(self, agent_id: str, limit: int = 20) -> List[Dict]:
        """
        Retrieves recent private LLM agent logs for display in the UI.
        """
        print(f"Executing get_agent_logs for agent_id: {agent_id}, limit: {limit}. Thread: {threading.get_ident()}")
        db_for_thread = None
        try:
            # Use a new DB connection for this thread to ensure thread safety
            db_for_thread = DBService(db_name=DB_NAME)
            db_for_thread.connect()
            
            log_entries: List[LLMLogEntry] = db_for_thread.get_logs_for_agent(agent_id, limit)
            
            # Convert LLMLogEntry objects to a list of dicts suitable for JSON serialization
            # and containing only fields needed by the frontend log summary.
            logs_for_ui = []
            for entry in log_entries:
                summary = entry.parsed_thinking_process if entry.parsed_thinking_process else entry.raw_llm_output
                if len(summary) > 150: # Truncate long summaries
                    summary = summary[:147] + "..."

                logs_for_ui.append({
                    "log_entry_id": entry.log_entry_id,
                    "timestamp_call_start": entry.timestamp_call_start.isoformat(), # Format as ISO string
                    "llm_call_type": entry.llm_call_type,
                    "summary": summary
                })
            
            print(f"JS API: Returning {len(logs_for_ui)} logs for agent {agent_id}.")
            return logs_for_ui
            
        except Exception as e:
            error_msg = f"Error fetching agent logs in API thread for agent {agent_id}: {e}"
            print(error_msg)
            import traceback
            traceback.print_exc()
            return [{"error": error_msg}] # Return list with error dict
        finally:
            if db_for_thread:
                db_for_thread.close()
                print(f"Closed DB connection for agent log request in thread {threading.get_ident()}")

    def get_active_tags_for_filter(self, limit: int = 10) -> List[Dict]:
        """
        Retrieves a list of recently active global tags for the filter UI.
        """
        print(f"Executing get_active_tags_for_filter, limit: {limit}. Thread: {threading.get_ident()}")
        db_for_thread = None
        try:
            db_for_thread = DBService(db_name=DB_NAME)
            db_for_thread.connect()
            
            active_tags_from_db: List[GlobalTag] = db_for_thread.get_recent_active_tags(limit=limit)
            
            tags_for_ui = []
            for tag_obj in active_tags_from_db:
                tags_for_ui.append({
                    "name": tag_obj.tag_name,
                    "count": tag_obj.occurrence_count
                    # 'selected' state will be managed by the frontend
                })
            
            print(f"JS API: Returning {len(tags_for_ui)} active tags for filter.")
            return tags_for_ui
            
        except Exception as e:
            error_msg = f"Error fetching active tags for filter in API thread: {e}"
            print(error_msg)
            import traceback
            traceback.print_exc()
            return [{"error": error_msg}] # Return list with error dict
        finally:
            if db_for_thread:
                db_for_thread.close()
                print(f"Closed DB connection for active tags request in thread {threading.get_ident()}")

    def filter_chat_history_by_tags(self, tags: List[str], limit: int = 100) -> List[Dict]:
        """
        Filters the chat history based on a list of tags.
        """
        print(f"Executing filter_chat_history_by_tags with tags: {tags}, limit: {limit}. Thread: {threading.get_ident()}")
        if not tags: 
            print("JS API: No tags provided for filtering, returning empty list.")
            return []

        db_for_thread = None
        try:
            db_for_thread = DBService(db_name=DB_NAME)
            db_for_thread.connect()
            
            filtered_messages_from_db: List[ChatMessage] = db_for_thread.get_chat_messages_by_tags(
                tags_to_filter=tags, 
                logic="OR", 
                limit=limit
            )
            
            history_for_ui = []
            for msg in filtered_messages_from_db:
                history_for_ui.append({
                    "message_id": msg.message_id,
                    "sender_nickname": msg.sender_nickname,
                    "content": msg.content,
                    "timestamp": msg.timestamp.isoformat(), 
                    "tags": msg.tags if msg.tags else [] 
                })
            
            print(f"JS API: Returning {len(history_for_ui)} messages filtered by tags: {tags}.")
            return history_for_ui
            
        except Exception as e:
            error_msg = f"Error filtering chat history by tags in API thread: {e}"
            print(error_msg)
            import traceback
            traceback.print_exc()
            return [{"error": error_msg}] 
        finally:
            if db_for_thread:
                db_for_thread.close()
                print(f"Closed DB connection for filter chat history request in thread {threading.get_ident()}")

if __name__ == '__main__':
    # For standalone testing, we need to mock or provide basic services
    # Need to import ParticipantConfig for the mock
    from core.data_models import ParticipantConfig

    class MockDBService:
        def connect(self): print("MockDBService connected.")
        def close(self): print("MockDBService closed.")
        def add_chat_message(self, msg): print(f"MockDB: Add chat: {msg.content}")
        def add_or_update_global_tag(self, tag, ts): print(f"MockDB: Add/Update tag: {tag}")
        def get_global_tag(self, tag): return GlobalTag(tag_name=tag, occurrence_count=1) # Simplified mock
        
        def get_recent_active_tags(self, limit: int = 10) -> List[GlobalTag]:
            print(f"MockDB: get_recent_active_tags called with limit {limit}")
            mock_tags_data = [
                GlobalTag(tag_name="python", occurrence_count=15, last_seen_timestamp=datetime.now(timezone.utc)),
                GlobalTag(tag_name="javascript", occurrence_count=10, last_seen_timestamp=datetime.now(timezone.utc)),
                GlobalTag(tag_name="vue", occurrence_count=8, last_seen_timestamp=datetime.now(timezone.utc)),
                GlobalTag(tag_name="ai", occurrence_count=20, last_seen_timestamp=datetime.now(timezone.utc)),
                GlobalTag(tag_name="testing", occurrence_count=5, last_seen_timestamp=datetime.now(timezone.utc)),
            ]
            return mock_tags_data[:limit]

        def get_chat_messages_by_tags(self, tags_to_filter: List[str], logic: str = "OR", limit: int = 100) -> List[ChatMessage]:
            print(f"MockDB: get_chat_messages_by_tags called with tags: {tags_to_filter}, logic: {logic}, limit: {limit}")
            mock_messages_data = [
                ChatMessage(message_id=str(uuid.uuid4()), participant_id="p1", sender_nickname="UserA", content="Message about python and ai", timestamp=datetime.now(timezone.utc), tags=["python", "ai"]),
                ChatMessage(message_id=str(uuid.uuid4()), participant_id="p2", sender_nickname="UserB", content="Vue is great for UI", timestamp=datetime.now(timezone.utc), tags=["vue", "javascript"]),
                ChatMessage(message_id=str(uuid.uuid4()), participant_id="p1", sender_nickname="UserA", content="Let's test python scripts", timestamp=datetime.now(timezone.utc), tags=["python", "testing"]),
                ChatMessage(message_id=str(uuid.uuid4()), participant_id="p3", sender_nickname="UserC", content="AI and machine learning", timestamp=datetime.now(timezone.utc), tags=["ai", "ml"]),
                ChatMessage(message_id=str(uuid.uuid4()), participant_id="p2", sender_nickname="UserB", content="Frontend with javascript", timestamp=datetime.now(timezone.utc), tags=["javascript", "frontend"]),
            ]
            
            if not tags_to_filter:
                return mock_messages_data[:limit]

            filtered = []
            for msg_item in mock_messages_data:
                # Ensure msg_item.tags is not None before checking
                current_tags = msg_item.tags if msg_item.tags else []
                if logic.upper() == "OR":
                    if any(tag in current_tags for tag in tags_to_filter):
                        filtered.append(msg_item)
                elif logic.upper() == "AND":
                    if all(tag in current_tags for tag in tags_to_filter):
                        filtered.append(msg_item)
            return filtered[:limit]

        def get_logs_for_agent(self, agent_id: str, limit: int = 20) -> List[LLMLogEntry]:
            print(f"MockDB: get_logs_for_agent called for {agent_id} with limit {limit}")
            dummy_logs = []
            if agent_id == "test_agent_01":
                for i in range(min(limit, 3)):
                    dummy_logs.append(LLMLogEntry(
                        log_entry_id=str(uuid.uuid4()),
                        agent_id=agent_id,
                        timestamp_call_start=datetime.now(timezone.utc) - timedelta(seconds=i*10),
                        timestamp_call_end=datetime.now(timezone.utc) - timedelta(seconds=i*10 - 5),
                        llm_call_type=f"test_call_type_{i}",
                        raw_llm_output=f"This is raw output for log {i}.",
                        parsed_thinking_process=f"This is parsed thinking for log {i}. It might be a bit long to demonstrate truncation."
                    ))
            return dummy_logs
        # --- Mocks for Whiteboard ---
        def add_whiteboard_item(self, item: WhiteboardItem):
            print(f"MockDB: Add whiteboard item: {item.display_name} ({item.reference_id})")
        
        def remove_whiteboard_item(self, reference_id: str) -> bool:
            print(f"MockDB: Remove whiteboard item: {reference_id}")
            return True # Assume success for mock
        
        def get_all_whiteboard_items(self) -> List[WhiteboardItem]:
            print("MockDB: get_all_whiteboard_items called")
            return []

    class MockAgentManager: pass
    
    class MockChatroomState:
        def __init__(self):
            self.participants = {"human_user_main": ParticipantConfig(participant_id="human_user_main", nickname="TestHuman", role="human")}
            self.public_chat_history = []
            self.global_tags = {}
            self.whiteboard_items: List[WhiteboardItem] = [] 
        
        def get_participant_by_id(self, pid): return self.participants.get(pid)

        def add_whiteboard_item(self, item: WhiteboardItem):
            if not any(existing_item.full_local_path == item.full_local_path for existing_item in self.whiteboard_items):
                self.whiteboard_items.append(item)
                print(f"MockChatroomState: Added whiteboard item '{item.display_name}' ({item.reference_id}). Total: {len(self.whiteboard_items)}")
            else:
                print(f"MockChatroomState: Whiteboard item with path '{item.full_local_path}' already exists. Not added again.")

        def remove_whiteboard_item(self, reference_id: str) -> bool:
            initial_len = len(self.whiteboard_items)
            self.whiteboard_items = [item for item in self.whiteboard_items if item.reference_id != reference_id]
            removed = len(self.whiteboard_items) < initial_len
            if removed:
                print(f"MockChatroomState: Removed whiteboard item '{reference_id}'. Total: {len(self.whiteboard_items)}")
            else:
                print(f"MockChatroomState: Whiteboard item '{reference_id}' not found for removal.")
            return removed

        def _preload_mock_whiteboard_items(self):
            if not self.whiteboard_items: 
                item1 = WhiteboardItem(reference_id="wb_mock_id_001", display_name="preloaded_doc.pdf", full_local_path="/test/preloaded_doc.pdf", uploader_participant_id="human_user_main")
                self.whiteboard_items.append(item1)
                print(f"MockChatroomState: Preloaded '{item1.display_name}'")


    mock_db = MockDBService()
    mock_agents = MockAgentManager()
    mock_state = MockChatroomState()
    
    mock_state._preload_mock_whiteboard_items()
        
    api = PywebviewApi(db_service=mock_db, agent_manager=mock_agents, chatroom_state=mock_state)
    print("PywebviewApi example run with mocks.")
    
    response = api.handle_human_message_submission("Hello from JS!", ["greeting", "test"])
    print(f"Response from handle_human_message_submission: {response}")
    
    print("\n--- Testing Whiteboard API ---")
    initial_data_resp = api.get_initial_chatroom_data()
    print(f"Response from get_initial_chatroom_data (with preloaded): {initial_data_resp}")
    assert len(initial_data_resp["whiteboard_items"]) == 1
    assert initial_data_resp["whiteboard_items"][0]["reference_id"] == "wb_mock_id_001"

    add_resp1 = api.add_whiteboard_item("/path/to/my_file.txt")
    print(f"Response from add_whiteboard_item ('my_file.txt'): {add_resp1}")
    assert add_resp1["success"]
    assert len(add_resp1["whiteboard_items"]) == 2 
    added_item1_ref_id = None
    for item_data in add_resp1["whiteboard_items"]:
        if item_data["full_local_path"] == "/path/to/my_file.txt":
            added_item1_ref_id = item_data["reference_id"]
            break
    assert added_item1_ref_id is not None

    add_resp2 = api.add_whiteboard_item("/another/path/image.png")
    print(f"Response from add_whiteboard_item ('image.png'): {add_resp2}")
    assert add_resp2["success"]
    assert len(add_resp2["whiteboard_items"]) == 3
    added_item2_ref_id = None
    for item_data in add_resp2["whiteboard_items"]:
        if item_data["full_local_path"] == "/another/path/image.png":
            added_item2_ref_id = item_data["reference_id"]
            break
    assert added_item2_ref_id is not None
    
    add_resp_duplicate = api.add_whiteboard_item("/path/to/my_file.txt")
    print(f"Response from add_whiteboard_item (duplicate 'my_file.txt'): {add_resp_duplicate}")
    assert add_resp_duplicate["success"] 
    assert len(add_resp_duplicate["whiteboard_items"]) == 3 

    remove_resp1 = api.remove_whiteboard_item(added_item1_ref_id)
    print(f"Response from remove_whiteboard_item ('{added_item1_ref_id}'): {remove_resp1}")
    assert remove_resp1["success"]
    assert len(remove_resp1["whiteboard_items"]) == 2
    current_ids = [item["reference_id"] for item in remove_resp1["whiteboard_items"]]
    assert added_item1_ref_id not in current_ids
    assert added_item2_ref_id in current_ids
    assert "wb_mock_id_001" in current_ids

    remove_non_existent_resp = api.remove_whiteboard_item("id_that_does_not_exist_123")
    print(f"Response from remove_whiteboard_item (non-existent): {remove_non_existent_resp}")
    assert not remove_non_existent_resp["success"] 
    assert len(remove_non_existent_resp["whiteboard_items"]) == 2 

    final_data_resp = api.get_initial_chatroom_data()
    print(f"Response from get_initial_chatroom_data (final state): {final_data_resp}")
    assert len(final_data_resp["whiteboard_items"]) == 2
    print("--- Whiteboard API Test Complete ---")

    print("\n--- Testing get_agent_logs ---")
    agent_logs_response = api.get_agent_logs(agent_id="test_agent_01", limit=2)
    print(f"Response from get_agent_logs for test_agent_01 (limit 2): {agent_logs_response}")
    assert len(agent_logs_response) == 2
    if agent_logs_response and "summary" in agent_logs_response[0]:
         assert "..." in agent_logs_response[0]["summary"] 

    empty_agent_logs = api.get_agent_logs(agent_id="non_existent_agent")
    print(f"Response from get_agent_logs for non_existent_agent: {empty_agent_logs}")
    assert len(empty_agent_logs) == 0
    print("--- Agent Logs Test Complete ---")

    print("\n--- Testing Tag Filter API ---")
    # Test get_active_tags_for_filter
    active_tags_resp = api.get_active_tags_for_filter(limit=3)
    print(f"Response from get_active_tags_for_filter (limit 3): {active_tags_resp}")
    assert len(active_tags_resp) == 3
    if active_tags_resp: # Check if list is not empty
        assert active_tags_resp[0]["name"] == "python"
        assert active_tags_resp[0]["count"] == 15

    active_tags_default_limit_resp = api.get_active_tags_for_filter() 
    print(f"Response from get_active_tags_for_filter (default limit): {active_tags_default_limit_resp}")
    assert len(active_tags_default_limit_resp) == 5 

    # Test filter_chat_history_by_tags
    filtered_history_resp_python = api.filter_chat_history_by_tags(tags=["python"], limit=5)
    print(f"Response from filter_chat_history_by_tags (tags=['python']): {filtered_history_resp_python}")
    assert len(filtered_history_resp_python) >= 1 
    for msg_item_py in filtered_history_resp_python:
        assert "python" in (msg_item_py.get("tags") or [])


    filtered_history_resp_vue_ai = api.filter_chat_history_by_tags(tags=["vue", "ai"], limit=5)
    print(f"Response from filter_chat_history_by_tags (tags=['vue', 'ai']): {filtered_history_resp_vue_ai}")
    assert len(filtered_history_resp_vue_ai) == 3 
    vue_found = False
    ai_found = False
    for msg_item_va in filtered_history_resp_vue_ai:
        current_tags_va = msg_item_va.get("tags") or []
        if "vue" in current_tags_va:
            vue_found = True
        if "ai" in current_tags_va:
            ai_found = True
    assert vue_found and ai_found

    filtered_history_empty_tags = api.filter_chat_history_by_tags(tags=[], limit=5)
    print(f"Response from filter_chat_history_by_tags (tags=[]): {filtered_history_empty_tags}")
    assert len(filtered_history_empty_tags) == 0 

    filtered_history_non_existent_tag = api.filter_chat_history_by_tags(tags=["nonexistenttag"], limit=5)
    print(f"Response from filter_chat_history_by_tags (tags=['nonexistenttag']): {filtered_history_non_existent_tag}")
    assert len(filtered_history_non_existent_tag) == 0
    
    print("--- Tag Filter API Test Complete ---")
    
    print("Finished PywebviewApi example run.")